#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/mempool.h>

#include <scsi/scsi_cmnd.h>
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <linux/version.h>

#include <linux/tcp.h>
#include <linux/uio.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/socket.h>

#include <linux/sched.h>
#include <linux/kthread.h>

#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>

#include <linux/cdev.h>

#include "ndmp_xdr.h"
#include "ndmp_wrappers.h"


#define NDMP_TCP_PORT 10000
#define NDMP_HBA_VERSION "0.0.4"

#define TAPE_DEVICE 0
#define ROBOT_DEVICE 1

#define NDMP_SCSI_PROC_NAME   "ndmphba"
#define NDMP_SCSI_MAX_CMD_LEN       16
#define NDMP_SCSI_CANQUEUE          1
#define NDMP_SCSI_MAX_LUNS          1
#define NDMP_SCSI_MAX_TARGETS       1
#define NDMP_SCSI_MAX_CHANNELS      0
#define NDMP_SCSI_CMDS_PER_LUN      1
#define NDMP_SCSI_MAX_SG      256
#define NDMP_SCSI_MAX_SECTORS     256

#define NDMP_SCSI_TASK_EMPTY 0
#define NDMP_SCSI_TASK_SUBMITTED 1
#define NDMP_SCSI_TASK_IN_PROGRESS 2
#define NDMP_SCSI_TASK_FINISHED 3
#define NDMP_SCSI_TASK_ABORTED 4

#define NDMP_SCSI_CONNECTION_FAILED 32
#define NDMP_SCSI_DEVICE_FAILED 48 
#define NDMP_SCSI_AUTH_FAILED 64

#define _NDMP_STAT_NR 0
#define _NDMP_STAT_IN_PROGRESS 1
#define _NDMP_STAT_FAILED 2
#define _NDMP_STAT_DONE 3

#define HBA_STATE_CONNECTION 0
#define HBA_STATE_AUTH 2
#define HBA_STATE_DEVICE 4
#define HBA_STATE_THREAD 6

#define NDMP_NET_IO_SUCCESS 0
#define NDMP_NET_IO_ERR 11
#define NDMP_NET_IO_INTR 12
#define NDMP_NET_IO_UNEXPECTED_RECORD 13
#define NDMP_NET_IO_CONNECT_FAIL 14
#define NDMP_XDR_ERROR 33;


#define NDMP_HBA_TIMEOUT (jiffies + (30 * HZ))

#define NDMP_SCSI_CONTROL_MAJOR 239

struct ndmp_scsi_session {
        struct list_head list;

	struct socket *socket;
	struct sockaddr addr;
	struct Scsi_Host *shost;

	char ndmp_server[16];
	char username[16];
	char password[16];
	char scsi_device[16];
	char device_type;

	unsigned int seq_number;
	unsigned int old_seq_number;
	unsigned char  session_id;
	unsigned long state;


        spinlock_t  task_lock; 
	struct list_head task_queue;
	wait_queue_head_t txq;

	struct ndmp_header nh;

	unsigned int rmh;
	unsigned char last_fragment;
	XDR xdr;
	unsigned short tcpwnd;

	struct task_struct      *ndmp_task;
	struct timer_list	watchdog_timer;

	struct kobject ko;


};

struct ndmp_scsi_task {
        struct list_head  queue;
        unsigned int seq_number;   
        unsigned int flags;
        struct scsi_cmnd *sc; 
        struct ndmp_scsi_session  *session; 
};

struct ndmp_scsi_sessions {
        struct list_head session_queue;
	unsigned char  last_id;
	struct semaphore session_lock; 
	struct kset *ks;
	struct cdev cdev;
	int dev_major;
};

struct ndmp_scsi_work_t {
        struct ndmp_scsi_session *session;
        struct work_struct work;
};


struct ndmp_scsi_task *ndmp_scsi_alloc_task(struct ndmp_scsi_session *session);
int ndmp_scsi_register_user_interface(void);
void ndmp_scsi_unregister_user_interface(void);
struct ndmp_scsi_session *ndmp_scsi_create_session(char *, char *,char *, char *, char);
void ndmp_scsi_destroy_session(struct ndmp_scsi_session *session);


int ndmp_auth ( struct ndmp_scsi_session *session);
int ndmp_exec_scsi_cmd (struct ndmp_scsi_task *task);
int ndmp_close_socket( struct ndmp_scsi_session *session);

void ndmp_scsi_complete_task(struct ndmp_scsi_task *task);
void ndmp_set_sensedata_illegal(struct scsi_cmnd *sc) ;
struct ndmp_scsi_task *ndmp_scsi_alloc_task(struct ndmp_scsi_session *session);
void ndmp_change_session_state (struct ndmp_scsi_session *session, char op, char state);
char ndmp_session_state (struct ndmp_scsi_session *session, char op);

int ndmp_recv_scsi_cmd (struct ndmp_scsi_task *task);

struct ndmp_scsi_session  *get_ndmp_session(int session_id);

int ndmp_thread (void *param);

void ndmp_scsi_watchdog(unsigned long param);
char __ndmp_session_state (struct ndmp_scsi_session *session, char op);
void __ndmp_change_session_state (struct ndmp_scsi_session *session, char op, char state);

int ndmp_create_kset(void);
void ndmp_destroy_session_kobj(struct ndmp_scsi_session *session);
int ndmp_create_session_kobj(struct ndmp_scsi_session *session);
void ndmp_scsi_kset_remove(void);
int ndmp_do_scsi_inquiry_and_open_device (struct ndmp_scsi_session *session);
int ndmp_close_device ( struct ndmp_scsi_session *session);

void ndmp_remove_session(struct ndmp_scsi_session *session);

void ndmp_set_sensedata_commfailure(struct scsi_cmnd *sc);

int kill_ndmp_thread(struct ndmp_scsi_session *session);
